IAM 評価論理ファン必見!AWS ドキュメントにリソースベースポリシー評価論理のプリンシパルごとの違いが記載されました
IAM の評価論理、完全に理解した。
コンバンハ、IAM 評価論理おじさん(幸)です。
私としたことがしばらく気づいていなかったのですが、2021年10月5日に IAM の AWS ドキュメントで激アツな更新がされていました。
(機械翻訳)リソースベースのポリシーや同一アカウント内の異なるプリンシパルタイプの影響についての情報を追加しました。
これまで、同一アカウントの IAM 評価論理でリソースベースポリシーによる Allow が最終的にどのような評価をもたらすか、「プリンシパルごとに異なる動作をする場合がある」という記載がされていました。
え……その詳細が分からないと困るのでは……という状態でしたが、ついにプリンシパルごとの挙動の違いが記載されました。
「プリンシパルとは何か」をより深く理解するためにとても有益な情報だったのですが、初見では全く理解が追いつかなかったので、なるべく噛み砕いてその内容を説明します。
まとめ
- IAM ロールを引き受けたセッションや IAM ユーザーをフェデレートしたユーザーをここではセッションプリンシパルと呼ぶ
- リソースベースポリシーの Principal 要素でセッションプリンシパルを指定した時と、そのセッションの基になるエンティティ(IAM ロール、IAM ユーザー)を指定した時とで評価の動作が異なる
- IAM 評価論理は難しい
IAM ロールとそれを引き受けたセッションを例にしたイメージは以下です。
ドキュメントに追記された内容をまとめると、こうです。
#2 と #5 のケースでは暗黙的な拒否になる、というのがポイントです。
リソースベースポリシーの評価論理のトピックが追記された
ドキュメントのどこが変わったかを確認しましょう。
ドキュメントでは以下のようなフローチャートで評価論理が説明されており、その少し下の方でリソースベースポリシーでの評価について記載されています。
このあたりが該当します。日本語版ではまだ更新がされていないため、ここで従来の記述を確認します。
IAM ユーザーまたはロールの ARN をリソースベースのポリシーのプリンシパルとして指定した場合、このロジックは異なる動作をする可能性があります。ロールを引き受けるか、ユーザーをフェデレートすると、セッションが生成されます。その場合、リソースベースのポリシーによって付与される有効なアクセス許可は、そのプリンシパルのアクセス許可境界またはセッションポリシーによって制限されます。引き受けたロールセッションまたはフェデレーティッドユーザーセッションの有効なアクセス許可の詳細については、「セッションポリシー」および「境界を設定した場合の有効なアクセス許可の評価」を参照してください。
え、何それ……むず……。
更新された記述を理解してからであれば「そういうことか」となりましたが、なかなか難しい表現です。「異なる動作をする可能性がある」と言われても困っちゃうな……という状態でした。
英語版に切り替えて新しい記述を確認します。ブラウザの機能で日本語に翻訳しています。
プリンシパルの種類ごとに、いくつかのパターンにおける評価論理の結果が表で示されています。「異なる動作をする可能性がある」とされていた部分が、明確に説明されるようになりました。
一番右の「理由」列を削り、便宜上番号を振った表が以下です。
# | リクエストを行うプリンシパル | リソースベースのポリシー | IDベースのポリシー | 権限の境界 | セッションポリシー | 結果 |
---|---|---|---|---|---|---|
1 | IAMの役割 | 適用できない | 適用できない | 適用できない | 適用できない | 適用できない |
2 | IAMロールセッション | 役割ARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 拒否 |
3 | IAMロールセッション | ロールセッションARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 許可する |
4 | IAMユーザー | IAMユーザーARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 適用できない | 許可する |
5 | IAMフェデレーションユーザー(GetFederationToken) | IAMユーザーARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 拒否 |
6 | IAMフェデレーションユーザー(GetFederationToken) | IAMフェデレーションユーザーセッションARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 許可する |
7 | rootユーザー | rootユーザーのARNを許可します | 適用できない | 適用できない | 適用できない | 許可する |
8 | AWSサービスプリンシパル | AWSサービスプリンシパルを許可します | 適用できない | 適用できない | 適用できない | 許可する |
え、何これ……むず……。
私は7回くらい読み直して理解できたので、なるべく皆さんには1回で理解していただけるよう説明していきます。
前提として押さえておくべき知識
今回の更新を理解するために前提として押さえておくべき知識を整理します。
同一アカウントにおける IAM 評価論理
IAM におけるポリシーは以下の 6 種類があります。
- アイデンティティベースポリシー
- リソースベースポリシー
- アクセス許可の境界(Permissions boundary)
- Organizations SCP
- アクセスコントロールリスト(ACL)
- セッションポリシー
プリンシパルが同一アカウントのリソースにアクションを実行する際、これらのポリシーが総合的に評価され、許可か拒否かの決定がなされます。
その評価論理をフローチャートで表したものが以下です。(ACL は特殊すぎるのでこのフローチャートでは現れません。)
アイデンティティベースポリシー
IAM アイデンティティである以下にアタッチできるポリシーで、いわゆる IAM ポリシー・Permissions policy がこれに該当します。
- IAM ユーザー
- IAM ロール
- IAM グループ
Permissions boundary
IAM エンティティのうち以下にアタッチし、権限に対して境界(上限)を定めるように機能します。
- IAM ユーザー
- IAM ロール
IAM グループにはアタッチできません。
Permissions boundary はアイデンティティベースポリシーとセットで評価に用いられます。基本的にはアイデンティティベースポリシーと Permissions boundary の双方で許可されていないとアクションは拒否(暗黙的な拒否)となります。
リソースベースポリシー
S3 バケットや SNS トピック、Lambda 関数など、アクションの実行先となる AWS リソースにアタッチするポリシーです。すべてのリソースタイプでリソースベースポリシーが使用できるわけではなく、リソースベースポリシーがサポートされているリソースは以下から確認できます。
同一アカウントでの評価論理において、リソースベースポリシーの評価の順序はアイデンティティベースポリシーや Permissions boundary より先です。
よって、Permissions boundary で許可がなくとも、リソースベースポリシーでの許可によってアクションが成功するケースもあります。以前にそのような内容をブログにしました。
上記のエントリではプリンシパルとして IAM ユーザーを使用した場合を取り上げています。今回のドキュメントの更新により、他のプリンシパルタイプを使用した場合の挙動が明確に記載されました。
IAM ロールを引き受けたセッション
リソースベースポリシーの Principal 要素では IAM ロールを指定できますが、IAM ロール自身がプリンシパル(アクションを実行する主体)となることはありません。
ロールを引き受けてアクションを実行する際、あくまでプリンシパルは IAM ロールを引き受けたセッションです。IAM ユーザーはもちろんのこと、外部の Idp や AWS サービスも IAM ロールを引き受けることができます。
ロールを引き受けたセッションの ARN は以下の形式で表されます。
arn:aws:sts::111122223333:assumed-role/examplerole/examplerolesessionname
一方、IAM ロールの ARN は以下の形式です。
arn:aws:iam::111122223333:role/examplerole
両者の違いをきちんと意識しておきましょう。
IAM ロールを引き受けたセッションは、IAM ロールにアタッチされていたアイデンティティベースポリシーと Permissions boundary を引き継ぎます。
フェデレーテッドユーザー
今回のエントリでは GetFederationToken API を使用して作成された一時的に有効なユーザーセッションを指します。
先ほどの「 IAM ロールを引き受けたセッション」は IAM ロールの権限を持ちますが、フェデレーテッドユーザーは IAM ユーザーと同じ権限を持つセッションです。アイデンティティベースポリシーや Permissions boundary を継承する点も変わりありません。
フェデレーテッドユーザーを作成している例は以下を参照してください。
Web ID フェデレーションや SAML 2.0 ベースのフェデレーションを使用する文脈でもフェデレーテッドユーザーという言葉は使用されますが、今回はそれは忘れてください。
今回のドキュメントの更新で記載されているフェデレーテッドユーザーはあくまで GetFederationToken によって既存の IAM ユーザーから生成したユーザーセッションを指します。
以下の ARN で表されます。
arn:aws:sts::111122223333:federated-user/exampleuser
セッションポリシー
IAM ロールや IAM ユーザーをベースに生成されたセッションには、セッション生成時にセッションポリシーを任意で割り当てられます。セッションは有効期限を持ちますので、そのセッション内のみで有効なポリシーです。
例えば AWS CLI で sts assume-role をする際にセッションポリシーを割り当てるには、--policy-arns
や--policy
オプションを使用します。
セッションポリシーの働きは Permissions boundary と同一です。アイデンティティベースポリシーが評価対象の時はセッションポリシーも評価対象になりますし、Permissions boundary もアタッチされているのであれば 3 つのポリシーすべてで Allow が無いとアクションは拒否(暗黙的な拒否)となります。
リソースベースポリシーの評価論理について再確認
前提の知識のインプットが終わったところで、先ほどの表を再度見てみましょう。
# | リクエストを行うプリンシパル | リソースベースのポリシー | IDベースのポリシー | 権限の境界 | セッションポリシー | 結果 |
---|---|---|---|---|---|---|
1 | IAMの役割 | 適用できない | 適用できない | 適用できない | 適用できない | 適用できない |
2 | IAMロールセッション | 役割ARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 拒否 |
3 | IAMロールセッション | ロールセッションARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 許可する |
4 | IAMユーザー | IAMユーザーARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 適用できない | 許可する |
5 | IAMフェデレーションユーザー(GetFederationToken) | IAMユーザーARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 拒否 |
6 | IAMフェデレーションユーザー(GetFederationToken) | IAMフェデレーションユーザーセッションARNを許可します | 暗黙の拒否 | 暗黙の拒否 | 暗黙の拒否 | 許可する |
7 | rootユーザー | rootユーザーのARNを許可します | 適用できない | 適用できない | 適用できない | 許可する |
8 | AWSサービスプリンシパル | AWSサービスプリンシパルを許可します | 適用できない | 適用できない | 適用できない | 許可する |
ここでは、実行しようとしているアクションに対して各種ポリシーの内訳が以下であるケースが想定されています。
ポリシー | 内容 |
---|---|
すべてのポリシー | 拒否がない |
Organizations SCP | 設定されていないか、許可されている |
リソースベースポリシー | 特定のプリンシパルにのみ許可している |
Permissions boundary | 設定されているが許可がない(あるいは適用できない) |
セッションポリシー | 設定されているが許可がない(あるいは適用できない) |
アイデンティティベースポリシー | 設定されているが許可がない(あるいは適用できない) |
つまり、評価論理のフローチャートでは以下まで進んでいる状態です。リソースベースポリシーで許可が与えられていれば最終評価が Allow になりますし、なければ最終評価は Deny (暗黙的な拒否)です。
「アクションを実行するプリンシパル」と「リソースベースポリシーの Principal 要素で指定されているプリンシパル」の組み合わせによって、リソースベースポリシーで許可が与えられるか否かが変わります。
表を少し加工して図に起こしてみたのが以下です。
#2 と #5 が暗黙的な拒否になる、という点だけ押さえられれば他はあまり重要ではないのですが、少し補足します。
プリンシパルとしての IAM ロール
#1 の部分です。先述の通り、IAM ロール自身がプリンシパルではなく IAM ロールを引き受けたセッションがプリンシパルである、ということを表しています。
リソースベースポリシーの Principal 要素で IAM ロールを指定できることと、IAM ロールがプリンシパルであることは別です。
root ユーザー
#7 が該当します。root ユーザーは最上位の権限を持っており、アイデンティティベースポリシーも Permissions boundary もセッションポリシーも指定できません。
同一アカウントのアクセスでリソースベースポリシーでの許可が必要か?についてはリソースの種別により異なる気がしています。今回は深堀りはやめておきます。
サービスプリンシパル
#8 が該当します。AWS サービスからのアクションを許可するにはリソースベースポリシーでサービスプリンシパルとして許可します。
例えば CloudTrail のログを S3 バケットに出力する場合、S3 バケットポリシーの Principal で"Service": "cloudtrail.amazonaws.com"
を指定します。
サービスプリンシパルにはアイデンティティベースポリシーも Permissions boundary もセッションポリシーも指定できないため、カスタマー側でコントロールできるのはリソースベースポリシーのみです。
セッションプリンシパル
ここではロールセッションとフェデレーテッドユーザーを指します。
#2 と #3、#5 と #6 はそれぞれ、プリンシパルがセッションプリンシパルであるときの、リソースベースポリシーの Principal 要素の指定の違いによる評価の差異を表しています。
セッションプリンシパルそのものをリソースベースポリシーで許可している場合、Permissions boundary やセッションポリシーは評価の対象になりません。
一方、セッションプリンシパルの基であるエンティティ(IAM ロール、IAMユーザー)がプリンシパルの場合、Permissions boundary やセッションポリシーも評価対象になります。
このあたりがドキュメントに明記されたのはとてもありがたいです。
やってみた
今回は以下の構成で試してみます。
EC2 インスタンスに IAM ロール(正確にはインスタンスプロファイル)をアタッチし、インスタンス上から S3 にアクションを行います。
IAM ロールのアイデンティティベースポリシーでは S3 の許可を与えません。その状態で、以下の組み合わせの試行を行います。
- Permissions boundary (アクセス許可の境界)をアタッチしたパターンとデタッチしたパターン
- S3 バケットポリシーで IAM ロールを許可したパターンとロールセッションを許可したパターン
Permissions boundary をアタッチするときは、もちろん S3 の許可は与えません。また、セッションポリシーは検証の対象から外しました。 結局は Permissions boundary と同じ働きをするので、どちらか一方だけあれば十分なためです。
先に結果を見ると以下のようになりました。
# | ID ベースポリシー | アクセス許可の境界 | リソースベースポリシーのプリンシパル | 結果 |
---|---|---|---|---|
Ⅰ | 許可なし | 未アタッチ | IAM ロール ARN | 許可 |
Ⅱ | 許可なし | 許可なし | IAM ロール ARN | 暗黙的な拒否 |
Ⅲ | 許可なし | 未アタッチ | ロールセッション ARN | 許可 |
Ⅳ | 許可なし | 許可なし | ロールセッション ARN | 許可 |
#Ⅰ のアクションが成功する、というのがややこしさに拍車をかけている気がします。
S3 バケットポリシーの Principal で IAM ロールを許可
S3 バケットchibayuki-hoge-hoge
のバケットポリシーを以下の通りに設定しました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::000000000000:role/AmazonSSMRoleForInstancesQuickSetup" }, "Action": "s3:*", "Resource": [ "arn:aws:s3:::chibayuki-hoge-hoge", "arn:aws:s3:::chibayuki-hoge-hoge/*" ] } ] }
Principal で指定しているのが EC2 インスタンスにアタッチ()されている IAM ロールです。
上記のロールには SSM の使用に必要な最小限のポリシーを割り当てています。s3:
プレフィックスを持つアクションは許可されていません。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ssm:DescribeAssociation", "ssm:GetDeployablePatchSnapshotForInstance", "ssm:GetDocument", "ssm:DescribeDocument", "ssm:GetManifest", "ssm:GetParameter", "ssm:GetParameters", "ssm:ListAssociations", "ssm:ListInstanceAssociations", "ssm:PutInventory", "ssm:PutComplianceItems", "ssm:PutConfigurePackageResult", "ssm:UpdateAssociationStatus", "ssm:UpdateInstanceAssociationStatus", "ssm:UpdateInstanceInformation" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "ssmmessages:CreateControlChannel", "ssmmessages:CreateDataChannel", "ssmmessages:OpenControlChannel", "ssmmessages:OpenDataChannel" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "ec2messages:AcknowledgeMessage", "ec2messages:DeleteMessage", "ec2messages:FailMessage", "ec2messages:GetEndpoint", "ec2messages:GetMessages", "ec2messages:SendReply" ], "Resource": "*" } ] }
この状態で EC2 に SSM 接続し、AWS CLI を実行すると、プリンシパルが IAM ロールを引き受けたセッションであることが確認できます。
sh-4.2$ aws sts get-caller-identity { "Account": "000000000000", "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2", "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2" }
IAM ロール名の後に続いているのがセッション名で、デフォルトでは EC2 インスタンス ID が設定されます。
Ⅰ. Permissions boundary なし
S3 バケットchibayuki-hoge-hoge
を指定し、ListObjects を実行します。
sh-4.2$ aws s3 ls chibayuki-hoge-hoge 2021-10-17 09:18:35 6 test.txt
問題なくアクションが成功しました。
IAM ロールのアイデンティティベースポリシーでは S3 に関する許可がないものの、リソースベースポリシー側で許可が与えられているために最終評価が許可となりました。
ただ、後続の結果と照らし合わせてみると「ロールセッションに直接許可が与えられているわけではない」ということになります。
Ⅱ. Permissions boundary あり
IAM ロールの Permissions boundary として、アイデンティティベースポリシーと同じAmazonSSMManagedInstanceCore
をアタッチします。
その状態で先ほどと同じ操作を実行します。
sh-4.2$ aws s3 ls chibayuki-hoge-hoge An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
アクセスは拒否されました。Permissions boundary が追加されたことで、リソースベースポリシーによる許可は優先されなくなりました。
Principal として IAM ロールを指定している時の挙動として、こういった部分を押さえておく必要がありそうです。
S3 バケットポリシーの Principal で ロールセッションを許可
続いて S3 バケットポリシーを以下のように変更します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2" }, "Action": "s3:*", "Resource": [ "arn:aws:s3:::chibayuki-hoge-hoge", "arn:aws:s3:::chibayuki-hoge-hoge/*" ] } ] }
Principal としてロールセッションを指定しました。ちなみにワイルドカードを使用して複数のセッションを指定するような書き方はできません。
Ⅲ. Permissions boundary なし
先ほどアタッチした Permissions boundary をデタッチし、アイデンティティベースポリシーのみの状態でアクセスを試行します。
sh-4.2$ aws sts get-caller-identity { "Account": "000000000000", "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2", "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2" } sh-4.2$ aws s3 ls chibayuki-hoge-hoge 2021-10-17 09:18:35 6 test.txt
ロールセッションを指定した時にも、リソースベースポリシーによる許可が正常に働きました。
Ⅳ. Permissions boundary あり
先ほどと同じように Permissions boundary としてAmazonSSMManagedInstanceCore
をアタッチします。
S3 へのアクセスを実行すると、問題なく成功しました。
sh-4.2$ aws sts get-caller-identity { "Account": "000000000000", "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2", "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2" } sh-4.2$ aws s3 ls chibayuki-hoge-hoge 2021-10-17 09:18:35 6 test.txt
#Ⅱ のケースと異なり「ロールセッションに直接許可が与えられている」状態のため、Permissions boundary が評価対象とならず、リソースベースポリシーでの許可によって最終評価が許可となりました。
# | ID ベースポリシー | アクセス許可の境界 | リソースベースポリシーのプリンシパル | 結果 |
---|---|---|---|---|
Ⅰ | 許可なし | 未アタッチ | IAM ロール ARN | 許可 |
Ⅱ | 許可なし | 許可なし | IAM ロール ARN | 暗黙的な拒否 |
Ⅲ | 許可なし | 未アタッチ | ロールセッション ARN | 許可 |
Ⅳ | 許可なし | 許可なし | ロールセッション ARN | 許可 |
このあたりの仕様を従来のドキュメントの記述のみで理解していた強者は いるのでしょうか……。
IAM の評価論理を理解する上で役立つ激アツ更新
AWS ドキュメント内の、同一アカウント評価論理のリソースベースポリシーの項のアップデートを確認しました。
クロスアカウントの場合は強制的にアイデンティティベースポリシーとリソースベースポリシーの双方で許可が必要であり、「リソースベースポリシーでのみ許可が与えられる」というケースがありません。そういった意味だと、セッションプリンシパルが絡んだ評価論理は同一アカウントの場合の方が複雑そうです。
これまで明確な記載がなく手探りでやっていくしかなかった部分が、詳細なレベルでドキュメントに記載されることになりました。今回の追記内容を見て、IAM エンティティをベースに生成された一時的な認証情報を用いたセッションがプリンシパルになる、ということの意味を理解できた気がします。個人的に激アツでした。
本エントリが、今回のドキュメントの更新内容を理解する手助けになっていれば幸いです。
以上、 チバユキ (@batchicchi) がお送りしました。